<?php
/**
 * Copyright (c) [2019] [吴跃忠]
 * [selibra] is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 * http://license.coscl.org.cn/MulanPSL
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v1 for more details.
 */
declare(strict_types=1);

namespace Selibra\Di\Container;


use Selibra\Tools\Console;
use Selibra\Di\AnnotationExecEntity;
use Selibra\Di\Annotations\SelibraAnnotationConstants;
use Selibra\Di\DI;
use Selibra\Di\Exception\NoImplementClass;
use Selibra\Di\reflection\Reflection;

class Container extends Protocol\ContainerProtocol
{

    /**
     * @var ContainerCollector
     */
    protected ContainerCollector $containerCollector;


    public function __construct()
    {
        $this->containerCollector = new ContainerCollector();
    }


    /**
     * @return ContainerCollector
     */
    public function collector()
    {
        return $this->containerCollector;
    }


    /**
     * @inheritDoc
     * @throws NoImplementClass
     */
    public function get($id, $metadata = null)
    {
        // 获取
        try {
            if (empty($metadata)) {
                $metadata = $this->containerCollector->get($id);
            }
            if ($metadata->getReflectionClass()->isInterface()) {
                // 是一个接口类，需要读取对应的接口
                $implementationClass = $this->getInterfaceImplementationClass($id);
                return DI::getObjectContext($implementationClass);
            }
            $reflection = new Reflection($id);
            $reflectClass = $reflection->getReflectClass();
            $constructorParametersValues = $reflection->getConstructorParametersValues();

            if (!empty($constructorParametersValues)) {
                $object = $reflectClass->newInstance(...$constructorParametersValues);
            } else {
                $object = $reflectClass->newInstance();
            }

            $properties = $reflectClass->getProperties();
            foreach ($properties as $property) {
                $annotations = $metadata->getPropertyAnnotations($property->getName());
                if (!empty($annotations)) {
                    $property->setAccessible(true);
                    foreach ($annotations as $annotation) {
                        $annotationExecEntity = new AnnotationExecEntity(SelibraAnnotationConstants::EXEC_CALL);
                        $annotationExecEntity->setProperty($property);
                        $annotationExecEntity->setObject($object);
                        $annotation->exec($annotationExecEntity);
                    }

                    if ($property->isPrivate() && $property->isProtected()) {
                        $property->setAccessible(false);
                    }
                }
            }
        } catch (\ReflectionException $e) {
            var_dump($e);
            return null;
        }
        return $object;
    }

    /**
     * @inheritDoc
     * @return bool
     */
    public function has($id)
    {
        $namespace = $this->containerCollector->get($id);
        if (empty($namespace)) {
            return false;
        }
        return true;
    }


    /**
     * @param $id
     * @return mixed
     * @throws NoImplementClass
     */
    public function getInterfaceImplementationClass($id)
    {
        // 是一个接口类，需要读取对应的接口
        $implementClasses = $this->collector()->getInterfaceImplementClasses($id);
        if (empty($implementClasses)) {
            throw new NoImplementClass("Interface \"" . $id . "\" did not find the corresponding implementation class");
        }
        return $implementClasses[0];
    }
}